package org.harsh.transmission.common.codec;

import cn.hutool.core.lang.UUID;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.harsh.common.Constants;
import org.harsh.common.Util;
import org.harsh.transmission.common.CustomKey;
import org.harsh.transmission.common.NodeQueue;

import java.io.IOException;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.util.concurrent.TimeUnit;

@Slf4j
public class EcdhServerHandler extends ChannelInboundHandlerAdapter {
    private BigInteger dA;

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().attr(Constants.encryptKeyValid).set(false);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.channel().eventLoop().scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    // 建立连接时，即发起交换密钥
                    ECPoint Qa;
                    try {
                        Qa = ECDH();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    //将ECPoint转换为字节数组
                    byte[] QaBytes = Qa.getEncoded(true);//使用压缩格式编码
                    long msgId = (long) Math.abs(UUID.randomUUID().hashCode());
                    CustomKey customKey = new CustomKey();
                    customKey.setMsgType((byte)Constants.MessageType.REQUPDATEKEY.getCode());
                    customKey.setMsgId(msgId);
                    // 此处更新版本信息
                    customKey.setVersion((byte)1);
                    customKey.setMsgMd5(Util.byteArrMD5(QaBytes));
                    customKey.setMsgData(QaBytes);
                    ctx.writeAndFlush(customKey);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        },1,1800l, TimeUnit.SECONDS);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof CustomKey) {
            // todo 此处缺少对 msgType 的判断
            CustomKey customKey = (CustomKey) msg;
            byte[] receivedBytes = customKey.getMsgData();
            // 解码字节数组为ECPoint对象
            // 将字节数组转换为十六进制字符串
            String receivedHex = Hex.toHexString(receivedBytes);
            // 将十六进制字符串转换为字节数组
            byte[] decodedBytes = Hex.decode(receivedHex);
            // 选择椭圆曲线参数
            ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
            // 创建曲线对象
            ECCurve curve = ecSpec.getCurve();
            // 解码字节数组为ECPoint对象
            ECPoint receivedNum = curve.decodePoint(decodedBytes);
            //计算共享密钥
            ECPoint sharedKeyA = receivedNum.multiply(dA).normalize();
            BigInteger result = sharedKeyA.getAffineXCoord().toBigInteger();

            log.info("Client PublicKey:" + receivedNum);
            log.info("Shared Key:" + result);
            ctx.channel().attr(Constants.aesEncryptKey).set(result);
            ctx.channel().attr(Constants.encryptKeyValid).set(true);
            long validTime = System.currentTimeMillis() + 3600 * 1000l;
            ctx.channel().attr(Constants.encryptKeyValidTime).set(validTime);
            ctx.channel().attr(Constants.encryptKeyVersion).set(customKey.getVersion());

            InetSocketAddress peerAddr = (InetSocketAddress)ctx.channel().remoteAddress();
            String peerIpAddr = peerAddr.getAddress().getHostAddress();
            int peerPort = peerAddr.getPort();
            //Channel channel, BigInteger pubKey, BigInteger sharedKey
            NodeQueue.insert(peerIpAddr, peerPort, ctx.channel(), dA, result);
        } else {
            ctx.fireChannelRead(msg);
        }
    }

    private ECPoint ECDH() throws Exception {
        // 添加 Bouncy Castle 作为安全提供程序
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        // 选择椭圆曲线参数
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
        // 生成密钥对
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
        //初始化密钥对生成器
        keyPairGenerator.initialize(ecSpec);
        ECPoint G = ecSpec.getG();
        BigInteger n = ecSpec.getN();
        //私钥dA
        dA = new BigInteger(n.bitLength(),new SecureRandom()).mod(n.subtract(BigInteger.ONE)).add(BigInteger.ONE);
        //公钥Qa
        return G.multiply(dA).normalize();
    }
}
